package net.jcip.chapter4;

import java.util.concurrent.Executors;
import java.util.concurrent.atomic.*;

/**
 * 代码清单 4-10  Number range class that does not sufficiently protect its invariants
 * 很多类 的各个属性 有时候并不是能够解耦,  他们可能相互组合
 * 本类 有 约束条件 即: 第一个数值 <= 第二个数值
 *
 *
 *  NumberRange不是线程安全的，没有维持对下界和上界进行约束的不变性条件。
 * @author Brian Goetz and Tim Peierls
 */

public class NumberRange {
    // INVARIANT: lower <= upper
    private final AtomicInteger lower = new AtomicInteger(0);

    private final AtomicInteger upper = new AtomicInteger(0);

    private void setLower(int i) {
        // Warning -- unsafe check-then-act
        if (i > upper.get()) {
            throw new IllegalArgumentException("can't set lower to " + i + " > upper");
        }
        lower.set(i);
    }

    /**
     *  setLower 和 setUpper等方法都尝试维持不变性条件，但却无法做到。setLower和setUpper都是“先检查
     * 后执行”的操作，但它们没有使用足够的加锁机制来保证这些操作的原子性。假设取值范围为
     * (0,10),如果一个线程调用setLower(5),而另一个线程调用setUpper(4),那么在一些错误的
     * 执行时序中，这两个调用都将通过检查，并且都能设置成功。结果得到的取值范围就是(5,4)，
     * 那么这是一个无效的状态。因此，虽然 AtomicInteger 是线程安全的，但经过组合得到的类却不
     * 是。由于状态变量lower和upper不是彼此独立的，因此NumberRange不能将线程安全性委托
     * 给它的线程安全状态变量。
     * NumberRange可以通过加锁机制来维护不变性条件以确保其线程安全性，例如使用一个锁
     * 来保护lower和 upper。此外，它还必须避免发布lower和 upper,从而防止客户代码破坏其不
     * 变性条件。
     * 如果某个类含有复合操作，例如NumberRange,那么仅靠委托并不足以实现线程安全性。
     * 在这种情况下，这个类必须提供自己的加锁机制以保证这些复合操作都是原子操作，除非整个
     * 复合操作都可以委托给状态变量。
     *
     *
     * */
    private void setUpper(int i) {
        // Warning -- unsafe check-then-act
        if (i < lower.get()) {
            throw new IllegalArgumentException("can't set upper to " + i + " < lower");
        }
        upper.set(i);
    }

    public boolean isInRange(int i) {
        return (i >= lower.get() && i <= upper.get());
    }


    public static void main(String[] args) {

        try {


            NumberRange numberRange = new NumberRange();

            for (int i = 0; i < Constant4.THREAD_NUM; i++) {

                int j = i;
                Constant4.executorService.execute(
                        () -> {
                            numberRange.setLower(j);
                            numberRange.setUpper(j);
                            Constant4.countDownLatch.countDown();
                        }
                );

            }

            Constant4.countDownLatch.await();
            Constant4.executorService.shutdown();
            System.out.println("numberRange.isInRange(100) = " + numberRange.isInRange(100));
        }catch (Exception e) {
            System.out.println("e.getMessage() = " + e.getMessage());
            e.getMessage();
        }finally {
            Constant4.executorService.shutdown();
        }
    }

}

